package configuration;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class ModuleEntry {
	//REP(c) = table !=null and has been initialized
	//AF(c) = table maps to a form of a Hash Tree
	private final ConcurrentHashMap<String, DBEntry> table;

	public ModuleEntry(final String nm) {
		//Requires: string !=null
		//Modifies: this
		//Effects: initializes this
		table = new ConcurrentHashMap<String, DBEntry>();
	}

	public void add(final DBEntry ad) throws EntryExists {
	    //* Requires: true
	    //* Modifies: this
	    //* Effects: Adds the DBEntry to this, otherwise throw new EntryExists Exception
		final String theID = ad.getID();
		if (table.containsKey(theID)) {
			throw new EntryExists();
		} else {
			table.put(theID, ad);
		}
	}

	public void addLeaf(final String ID, final String key, final String value)
			throws EntryExists {
	    //* Requires: true
	    //* Modifies: this
	    //* Effects: Adds the specified values to this, otherwise throw EntryExists Exception
		if (isID(ID)) {
			table.get(ID).add(key, value);
		} else {
			final DBEntry temp = new DBEntry(ID);
			temp.add(key, value);
			add(temp);
		}
	}

	public String getEntry(final String id, final String tag)
			throws NoEntryException {
	    //* Requires: true
	    //* Modifies: none
	    //* Effects: Returns the value associated with the id and tag, otherwise throw NoEntryException
		if (this.isEntry(id, tag)) {
			final DBEntry temp = table.get(id);
			return temp.getEntry(tag);
		} else {
			throw new NoEntryException();
		}

	}

	public boolean isEntry(final String key) {
	    //* Requires: true
	    //* Modifies: none
	    //* Effects: Returns true if key is immediate child of this, otherwise returns false
		if (table.containsKey(key)) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isEntry(final String key, final String tag) {
		if (table.containsKey(key)) {
		    //* Requires: true
		    //* Modifies: none
		    //* Effects: Returns true if (key, tag) exists in this, otherwise returns false
			return table.get(key).isTag(tag);
		} else {
			return false;
		}
	}

	public boolean isID(final String key) {
	    //* Requires: true
	    //* Modifies: none
	    //* Effects: Returns true if key is immediate child of this, otherwise returns false
		//WHY DO WE HAVE THIS(rhetorical as I wrote it), it seems to do same as 1st isEntry
		return table.containsKey(key);
	}

	public void removeID(final String key) throws NoEntryException {
		if (table.containsKey(key)) {
			table.remove(key);
		} else {
			throw new NoEntryException();
		}

	}
	public Set<String> getIDs() {
		//* Requires: true
	    //* Modifies: none
		//* Effects: Returns set of IDs associated with this
		return table.keySet();
	}
	public void addID(String ID) throws EntryExists {
		if(table.containsKey(ID)) {
			throw new EntryExists();
		}
		else {
			DBEntry temp = new DBEntry(ID);
			add(temp);
		}
	}
	public void removeTag(final String key, final String tag)
			throws NoEntryException {
	    //* Requires: true
	    //* Modifies: this
	    //* Effects: Removes specified entry, otherwise throw NoEntryException 
		if (this.isEntry(key, tag)) {
			table.get(key).removeTag(tag);
		} else {
			throw new NoEntryException();
		}

	}

	public void write(final BufferedWriter notebook) {
		//* Requires: true
	    //* Modifies: none
	    //* Effects: Writes data to notebook, otherwise pass IOException
		try {
			final HashMap<String, DBEntry> temp = new HashMap<String, DBEntry>(
					table);
			for (final String key : temp.keySet()) {
				notebook.write("<Identifier>");
				notebook.write(key);
				temp.get(key).write(notebook);
				notebook.write("</Identifier>");
				notebook.newLine();
			}
		}

		catch (final IOException e) {
			new IOException();
		}
	}

	public Set<String> getTags(final String ID) throws NoEntryException {
	    //* Requires: true
	    //* Modifies: none
	    //* Effects: Returns tags associated with given ID, otherwise throw NoEntryException
		if (table.containsKey(ID)) {
			return table.get(ID).getTags();
		} else {
			throw new NoEntryException();
		}
	}

	public void changeEntry(final String ID, final String tag,
			final String value) throws NoEntryException {
		 //* Requires: true
	    //* Modifies: this
	    //* Effects: Changes value associated with given tag and ID, otherwise throws NoEntryException
		if (table.containsKey(ID)) {
			table.get(ID).changeEntry(tag, value);
		} else {
			throw new NoEntryException();
		}
	}
}